Completed
Push — dev ( d30456...4fd03f )
by Fike
33s
created

_common.js ➔ ... ➔ Object.forEach   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 2
nop 1
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
1
/**
2
 * @module logger/_common
3
 */
4
5
/**
6
 * @enum
7
 * @readonly
8
 */
9
var Level = {
10
  All: 'ALL',
11
  Trace: 'TRACE',
12
  Debug: 'DEBUG',
13
  Notice: 'NOTICE',
14
  Info: 'INFO',
15
  Warn: 'WARN',
16
  Error: 'ERROR',
17
  Off: 'OFF'
18
}
19
20
/**
21
 * @enum
22
 * @readonly
23
 */
24
var Threshold = {
25
  ALL: 1,
26
  TRACE: 2,
27
  DEBUG: 3,
28
  INFO: 4,
29
  NOTICE: 5,
30
  WARN: 6,
31
  ERROR: 7,
32
  OFF: 8
33
}
34
35
/**
36
 * Converts input to appropriate level
37
 *
38
 * @param {Level|Threshold|string} level
39
 * @return {Level}
40
 */
41
function level (level) {
42
  if (level) {
43
    Object.keys(Threshold).forEach(function (key) {
44
      if (Threshold[key] === level || level.toString().toUpperCase() === key) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if Threshold.key === level ...).toUpperCase() === key is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
45
        return key
46
      }
47
    })
48
  }
49
  return Level.All
50
}
51
52
/**
53
 * Converts unknown input into threshold
54
 *
55
 * @param {Level|Threshold|string} lvl
56
 * @return {Threshold}
57
 */
58
function threshold (lvl) {
59
  return Threshold[level(lvl)]
60
}
61
62
/**
63
 * This class represents basic tree branch consisting of a node and (optionally)
64
 * child nodes. It is created with sole purpose to provide easy support for
65
 * package hierarchy tooling, so `ama-team.voxengine` would be presented as node
66
 * (root) -> (ama-team) -> (voxengine), and it's properties would be stored as
67
 * trees, allowing easy and configurable overlapping.
68
 *
69
 * @class
70
 *
71
 * @template {V} Wrapped value type
72
 *
73
 * @param {V|null} value Initial value
74
 */
75
function TreeNode (value) {
76
  var self = this
77
  /**
78
   * @template {V}
79
   * @typedef {Object.<string, TreeNode<V>>}
80
   */
81
  var children = {}
82
83
  /**
84
   * Adds new child to current node.
85
   *
86
   * @param {string} name Child name
87
   * @param {TreeNode<V>} child Child value
88
   * @return {TreeNode<V>} Returns added child
89
   */
90
  this.addChild = function (name, child) {
91
    children[name] = child
92
    return child
93
  }
94
95
  /**
96
   * Removes child
97
   *
98
   * @param {string} name Child name
99
   * @return {TreeNode<V>|null} Removed child value
100
   */
101
  this.removeChild = function (name) {
102
    var value = children[name]
103
    delete children[name]
104
    return value || null
105
  }
106
107
  /**
108
   * Fetches current node child by it's name.
109
   *
110
   * @param {string} name Child name.
111
   *
112
   * @return {TreeNode.<V>|null} Existing node or nothing.
113
   */
114
  this.child = function (name) { return children[name] || null }
115
116
  /**
117
   * Sets value for current node.
118
   *
119
   * @param {V} v
120
   * @return {TreeNode} Returns current instance.
121
   */
122
  this.set = function (v) {
123
    value = v
124
    return self
125
  }
126
127
  /**
128
   * Returns current node value.
129
   *
130
   * @return {V}
131
   */
132
  this.get = function () { return typeof value === 'undefined' ? null : value }
133
134
  /**
135
   * Retrieves node at path
136
   *
137
   * @param {string[]} path
138
   * @return {TreeNode<V>|null}
139
   */
140
  this.traverse = function (path) {
141
    if (path.length === 0) {
142
      return this
143
    }
144
    var name = path.shift()
145
    var cursor = this
146
    while (name && cursor) {
147
      cursor = cursor.child(name)
148
      name = path.shift()
149
    }
150
    return cursor || null
151
  }
152
153
  /**
154
   * Picks branch as a list of nodes (starting from the root) that match
155
   * requested path the most. In the best case, branch would contain root and
156
   * all requested nodes, in the worst - branch would consist of root only.
157
   *
158
   * @param {string[]} path Branch path as list of child names.
159
   * @return {[TreeNode<V>]} Picked branch.
160
   */
161
  this.branch = function (path) {
162
    var branch = [self]
163
    var cursor = self
164
    var name = path.shift()
165
    while (name) {
166
      cursor = cursor.child(name)
167
      if (!cursor) {
168
        break
169
      }
170
      branch.push(cursor)
171
      name = path.shift()
172
    }
173
    return branch
174
  }
175
176
  /**
177
   * Puts provided value at designated path down the tree.
178
   *
179
   * @param {string[]} path Path at which value has to be set.
180
   * @param {V} value Value to be set.
181
   * @return {TreeNode<V>} Created or updated node.
182
   */
183
  this.put = function (path, value) {
184
    var cursor = self
185
    var name = path.shift()
186
    while (name) {
187
      if (!cursor.child(name)) {
188
        cursor.addChild(name, new TreeNode(null))
189
      }
190
      cursor = cursor.child(name)
191
      name = path.shift()
192
    }
193
    return cursor.set(value)
194
  }
195
196
  /**
197
   * Fetches value closest to provided path. If node contains falsey value, it
198
   * is skipped.
199
   *
200
   * @param {string[]} path Path to pick the branch.
201
   * @return {V|null} Target value or null
202
   */
203
  this.retrieve = function (path) {
204
    return self.branch(path).reverse().reduce(function (carrier, node) {
205
      return carrier || node.get()
206
    }, null)
207
  }
208
209
  /**
210
   * Removes node at path and returns it's value
211
   * @param {string[]} path
212
   * @return {V|undefined}
213
   */
214
  this.remove = function (path) {
215
    if (path.length === 0) {
216
      throw new Error('You can\'t remove root node')
217
    }
218
    var segment = path.pop()
219
    var node = this.traverse(path)
220
    var child = node ? node.removeChild(segment) : null
221
    return child ? child.get() : null
222
  }
223
}
224
225
/**
226
 * This class acts as a structured closure in which logger may operate.
227
 *
228
 * It encloses logger settings - currently writer and threshold - for every
229
 * specified package, so loggers refer to context they were associated in to
230
 * find their settings, and end user may configure any logger he wants without
231
 * knowing about those loggers (unless particular level has been associated
232
 * with particular logger - in that case end user has to override it directly).
233
 *
234
 * @class
235
 * @template T
236
 * @implements ILoggerContext.<T>
237
 *
238
 * @param {Level} [lvl] Root threshold, {@see Level.Info} by default
239
 * @param {IWritable} [writer]
240
 */
241
function Context (lvl, writer) {
242
  var self = this
243
  var writers
244
  var levels
245
246
  function reset (lvl, writer) {
247
    writers = new TreeNode(writer || Logger)
248
    levels = new TreeNode(level(lvl) || Level.Info)
249
  }
250
251
  reset(lvl, writer)
252
253
  this.reset = reset
254
255
  function path (name) {
256
    return (name || '').toString().split('.').filter(function (_) { return _ })
257
  }
258
259
  this.path = path
260
261
  /**
262
   * Retrieves level for provided logger
263
   *
264
   * @param {string} name
265
   * @return {Level}
266
   */
267
  this.getLevel = function (name) { return levels.retrieve(path(name)) }
268
269
  /**
270
   * Retrieves level for provided logger
271
   *
272
   * @deprecated
273
   *
274
   * @param {string} name
275
   * @return {Level}
276
   */
277
  this.getThreshold = this.getLevel
278
279
  /**
280
   * Sets level for specified logger
281
   *
282
   * @param {string} [name]
283
   * @param {Level} lvl
284
   */
285
  this.setLevel = function (name, lvl) {
286
    if (!lvl) {
287
      lvl = name
288
      name = null
289
    }
290
    levels.put(path(name), level(lvl))
291
    return self
292
  }
293
294
  /**
295
   * Sets level for specified logger
296
   *
297
   * @deprecated
298
   *
299
   * @param {string} [name]
300
   * @param {Level} level
301
   */
302
  this.setThreshold = this.setLevel
303
304
  /**
305
   * Removes and returns level at provided path
306
   *
307
   * @param {string} name
308
   * @return {Level|undefined}
309
   */
310
  this.removeLevel = function (name) {
311
    var segments = path(name)
312
    if (segments.length === 0) {
313
      throw new Error('You can\'t remove default writer')
314
    }
315
    return levels.remove(segments)
316
  }
317
318
  /**
319
   * @param {string} name
320
   *
321
   * @return {IWritable}
322
   */
323
  this.getWriter = function (name) { return writers.retrieve(path(name)) }
324
325
  /**
326
   * @param {string} [name] Logger name
327
   * @param {IWritable} writer
328
   */
329
  this.setWriter = function (name, writer) {
330
    if (!writer) {
331
      writer = name
332
      name = null
333
    }
334
    writers.put(path(name), writer)
335
    return self
336
  }
337
338
  /**
339
   * Removes and returns writer under provided path
340
   *
341
   * @throws If attempted to remove root writer
342
   *
343
   * @param {string} name
344
   * @return {IWritable|undefined}
345
   */
346
  this.removeWriter = function (name) {
347
    var segments = path(name)
348
    if (segments.length === 0) {
349
      throw new Error('You can\'t remove default writer')
350
    }
351
    return writers.remove(segments)
352
  }
353
354
  /**
355
   * @abstract
356
   * @function Context#create
357
   * @param {string} name
358
   * @param {Level} [threshold]
359
   * @param {IWritable} [writer]
360
   * @return {T}
361
   */
362
}
363
364
/**
365
 * @namespace
366
 */
367
module.exports = {
368
  Level: Level,
369
  Threshold: Threshold,
370
  level: level,
371
  threshold: threshold,
372
  Context: Context,
373
  TreeNode: TreeNode
374
}
375